#pragma once
#include <list>
#include "vector.h"
#include "boundingsphere.h"

/**
 * Klasa otaczajacego prostopadloscianu
 *
 * @author Krzysztof Klein
 * @version 0.1
 */
class BoundingBox
{

	// @{
	/**
	 * Punkt brzegowy
	 */
	Vector minPoint;
	Vector maxPoint;
	// @}

public:

	/**
	 * Konstruktor domyslny
	 */
	BoundingBox() : minPoint(0.0f, 0.0f, 0.0f), maxPoint(0.0f, 0.0f, 0.0f) { }

	/**
	 * Konstruktor z punktami brzegowymi
	 *
	 * @param minPoint Bliski punkt brzegowy
	 * @param maxPoint Daleki punkt brzegowy
	 */
	BoundingBox(const Vector &minPoint, const Vector &maxPoint) : minPoint(minPoint), maxPoint(maxPoint) { }

	/**
	 * Konstruktor z sfera otaczajaca
	 *
	 * @param sphere Sfera
	 */
	BoundingBox(const BoundingSphere &sphere)
	{
		Vector d = Vector(1.0f, 1.0f, 1.0f) * sphere.getRadius();
		minPoint = sphere.getCenter() - d;
		maxPoint = sphere.getCenter() + d;
	}

	/**
	 * Konstruktor z lista punktow
	 *
	 * @param points Lista punktow
	 */
	BoundingBox(const std::list<Vector> &points)
	{
		std::list<Vector>::const_iterator it;

		// brak punktow?
		if(points.size() == 0)
		{
			minPoint = maxPoint = Vector(0.0f, 0.0f, 0.0f);
			return;
		}

		// pierwszy punkt
		it = points.begin();
		minPoint = maxPoint = (*it);
		it++;

		// dodaj reszte punktow
		for(; it != points.end(); it++)
		{
			addInternalPoint(*it);
		}
	}


	/**
	 * Punkt brzegu (bliski)
	 *
	 * @return Bliski punkt brzegu
	 */
	Vector& getMinPoint() { return minPoint; }

	/**
	 * Punkt brzegu (bliski)
	 *
	 * @return Bliski punkt brzegu
	 */
	const Vector& getMinPoint() const { return minPoint; }


	/**
	 * Punkt brzegu (daleki)
	 *
	 * @return Daleki punkt brzegu
	 */
	Vector& getMaxPoint() { return maxPoint; }

	/**
	 * Punkt brzegu (daleki)
	 *
	 * @return Daleki punkt brzegu
	 */
	const Vector& getMaxPoint() const { return maxPoint; }


	/**
	 * Srodek prostopadloscianu
	 *
	 * @return Srodek prostopadloscianu
	 */
	Vector getCenter() { return (maxPoint + minPoint) / 2.0f; }


	/**
	 * Wymiary prostopadloscianu
	 *
	 * @return Wymiary prostopadloscianu
	 */
	Vector getExtent() { return maxPoint - minPoint; }


	/**
	 * Sprawdza czy jest kolizja ze sfera
	 *
	 * @param sphereCenter Srodek sfery
	 * @param sphereRadius Promien sfery
	 * @return True jesli jest kolizja. False jesli nie.
	 */
	bool isCollidingWithSphere(const Vector &sphereCenter, float sphereRadius = 1.0f) const
	{
		BoundingBox box(BoundingSphere(sphereCenter, sphereRadius));
		return isCollidingWithBox(box.minPoint, box.maxPoint);
	}


	/**
	 * Sprawdza czy jest kolizja z prostopadloscianem
	 *
	 * @param boxMin Bliski brzeg
	 * @param boxMax daleki brzeg
	 * @return True jesli jest kolizja. False jesli nie.
	 */
	bool isCollidingWithBox(const Vector &boxMin, const Vector &boxMax) const
	{
		return minPoint <= boxMax && maxPoint >= boxMin;
	}


	/**
	 * Dodaje punkt do prostopadloscianu.
	 * Jesli punkt 'wystaje' poza prostopadloscian to go powieksza.
	 *
	 * @param x Wspolrzedna X punktu
	 * @param y Wspolrzedna Y punktu
	 * @param z Wspolrzedna Z punktu
	 */
	void addInternalPoint(float x, float y, float z)
	{
		// max
		if(x > maxPoint.getX()) maxPoint.setX(x);
		if(y > maxPoint.getY()) maxPoint.setY(y);
		if(z > maxPoint.getZ()) maxPoint.setZ(z);

		// min
		if(x < minPoint.getX()) minPoint.setX(x);
		if(y < minPoint.getY()) minPoint.setY(y);
		if(z < minPoint.getZ()) minPoint.setZ(z);
	}


	/**
	 * Dodaje punkt do prostopadloscianu.
	 * Jesli punkt 'wystaje' poza prostopadloscian to go powieksza.
	 *
	 * @param v Punkt
	 */
	void addInternalPoint(const Vector &v)
	{
		addInternalPoint(v.getX(), v.getY(), v.getZ());
	}


	/**
	 * Dodaje prostopadloscian.
	 * Jesli punkty 'wystaja' poza prostopadloscian to go powieksza.
	 *
	 * @param box Prostopadloscian
	 */
	void addInternalBox(const BoundingBox &box)
	{
		addInternalPoint(box.getMinPoint());
		addInternalPoint(box.getMaxPoint());
	}

};
